﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading;
using System.Windows;
using System.Windows.Forms;
using AGB.IDemonbuddy;
using Demonbuddy.MeshTool;
using Zeta;
using Zeta.Common;
using Zeta.Common.Plugins;
using Zeta.Internals;
using Zeta.Internals.SNO;
using Application = System.Windows.Forms.Application;
using MapViewerConstants = Demonbuddy.MapViewerConstants;

namespace AGB.Pathing
{
    public class AGBCartographer : IPlugin
    {

        #region Iplugin

        private Dictionary<int, CachedScene> cScenes; 

        private int lastCacheCount = 0;

                private void GetWorldBounds(IEnumerable<Demonbuddy.MeshTool.CachedScene> scenes, out Vector2 low, out Vector2 high)
        {
            float maxValue = float.MaxValue;
            float y = float.MaxValue;
            float minValue = float.MinValue;
            float num4 = float.MinValue;
            foreach (var scene in scenes)
            {
                if (scene != null)
                {
                    Vector2 zoneMin = scene.ZoneMin;
                    Vector2 zoneMax = scene.ZoneMax;
                    if (zoneMin.X < maxValue)
                    {
                        maxValue = zoneMin.X;
                    }
                    if (zoneMax.X < maxValue)
                    {
                        maxValue = zoneMax.X;
                    }
                    if (zoneMin.X > minValue)
                    {
                        minValue = zoneMin.X;
                    }
                    if (zoneMax.X > minValue)
                    {
                        minValue = zoneMax.X;
                    }
                    if (zoneMin.Y < y)
                    {
                        y = zoneMin.Y;
                    }
                    if (zoneMax.Y < y)
                    {
                        y = zoneMax.Y;
                    }
                    if (zoneMin.Y > num4)
                    {
                        num4 = zoneMin.Y;
                    }
                    if (zoneMax.Y > num4)
                    {
                        num4 = zoneMax.Y;
                    }
                }
            }
            low = new Vector2(maxValue, y);
            high = new Vector2(minValue, num4);
        }

              private PointF ConvertToPoint(SizeF pbSize, Vector2 low, Vector2 high, Vector2 v)
        {
            return new PointF(this.Conversion_1(0f, pbSize.Width, this.Conversion_2(low.Y, high.Y, v.Y)), this.Conversion_1(0f, pbSize.Height, this.Conversion_2(low.X, high.X, v.X)));
        }


        private float Conversion_1(float min, float max, float amount)
        {
            return (min + ((max - min) * amount));
        }

        private float Conversion_2(float min, float max, float amount)
        {
            return ((amount - min) / (max - min));
        }

        private void ConvertBounds(ref PointF min, ref PointF max)
        {
            float num = Math.Min(min.X, max.X);
            float num2 = Math.Min(min.Y, max.Y);
            float num3 = Math.Max(min.X, max.X);
            float num4 = Math.Max(min.Y, max.Y);
            min.X = num;
            min.Y = num2;
            max.X = num3;
            max.Y = num4;
        }

        public void DumpNavCells()
        {


            int step = 0;

            //Logging.Write("CachedScenes");

            Vector2 min;
            Vector2 max;
            var sceneList = new List<CachedScene>(cScenes.Values);

            GetWorldBounds(sceneList, out min, out max);


            Logging.Write(String.Format("{0}/{1} Size: {2}*{3}", min.X,min.Y,max.X,max.Y));

            Bitmap pt = new Bitmap((int)max.X, (int)max.Y);

            Graphics g = Graphics.FromImage(pt);

            int a = Environment.TickCount;

            foreach (var cachedSceneEntry in cScenes)
            {
                var cachedScene = cachedSceneEntry.Value;

                cachedScene.UpdateNavcells();
                
                int i = 0;
                foreach (var collision in cachedScene.NavCells)
                {
                    PaintNavCells(g, collision, new SizeF(max.X, max.Y), min, max, cachedScene.ZoneMin, 1, i,cachedScene);
                    i++;
                }                
            }


            pt.Save( this.ToString()+ ".bmp");
        }

        public void PaintNavCells(Graphics graphics, NavCell cell, SizeF size, Vector2 low, Vector2 high, Vector2 min, float scale, int index, Demonbuddy.MeshTool.CachedScene scene)
        {
            Vector2 v = min + new Vector2(cell.Min.X, cell.Min.Y);
            Vector2 vector2 = min + new Vector2(cell.Max.X, cell.Max.Y);
            PointF tf = this.ConvertToPoint(size, low, high, v);
            PointF max = this.ConvertToPoint(size, low, high, vector2);
            this.ConvertBounds(ref tf, ref max);

            if (!cell.Flags.HasFlag(NavCellFlags.AllowWalk)) return;

            graphics.FillRectangle(MapViewerConstants.WalkableBrush , (float) (tf.X*scale), (float) (tf.Y*scale), (float) ((max.X - tf.X)*scale), (float) ((max.Y - tf.Y)*scale));
            graphics.DrawRectangle(MapViewerConstants.BlackPen, (float)(tf.X * scale), (float)(tf.Y * scale), (float)((max.X - tf.X) * scale), (float)((max.Y - tf.Y) * scale));

        }

        public bool Equals(IPlugin other)
        {
            // we should probably be comparing versions too
            return other.Name == Name;
        }

        public string Author
        {
            get { return "Kane49"; }
        }

        public string Description
        {
            get { return "Draws maps for you :)"; }
        }

        public string Name
        {
            get { return "AGB.Cartographer"; }
        }

        public Version Version
        {
            get { return new Version(1, 0); }
        }

        public Window DisplayWindow
        {
            get { return null; }
        }

        /// <summary> Executes the shutdown action. This is called when the bot is shutting down. (Not when Stop() is called) </summary>
        public void OnShutdown()
        {
        }

        /// <summary> Executes the enabled action. This is called when the user has enabled this specific plugin via the GUI. </summary>
        public void OnEnabled()
        {
            lastCacheCount = 0;
            cScenes = new Dictionary<int, CachedScene>(); 
        }

        /// <summary> Executes the disabled action. This is called whent he user has disabled this specific plugin via the GUI. </summary>
        public void OnDisabled()
        {

        }

        #endregion

        private Type _currentAssembly;

        public void OnInitialize()
        {

        }

        public void OnPulse()
        {
            if (!ZetaWrap.IsInGame() && ZetaDia.IsLoadingWorld) return;
            bool newScene = false;

            foreach (var scene in ZetaDia.Scenes.GetScenes())
            {
                if (!cScenes.ContainsKey(scene.SceneGuid))
                {
                    cScenes.Add(scene.SceneGuid,new CachedScene(scene));
                    newScene = true;
                }
            }

            if ( newScene )
            {
                Logging.Write("Dumping new Map Data");
                DumpNavCells();
            }
        }
    }

}
